<?php
/**
 * 
 * alipay.class.php (wap支付宝支付模块)
 *
 */

class Alipay extends PaymentBase {
	public $config = array()  ;
	 /* 支付宝网关 */
	var $_gateway = 'http://wappaygw.alipay.com/service/rest.htm?';
    var $_code    = 'alipay';
	var $sign_type= 'MD5'; 

    public function __construct($config=array()) {
		parent::__construct($config);	//初始化父类构造
		//var_dump($this->_config);exit;
    }

	public function get_payform($order_info=[]){
		$notify_url = $this->_create_notify_wap_url($order_info['order_id'],$order_info['order_type'],false);	//异步通知地址
		$return_url = $this->_create_return_url($order_info['order_id'],$order_info['order_type'],false);	//回调地址
		$merchant_url = $return_url;	//操作中断返回地址
		$sign_type = $this->sign_type;	//0001<=>RSA 两者都可以
		$format = 'xml';	//请求参数格式
		$v = '2.0';			//接口版本号

		//请求业务数据
		$req_data = '<direct_trade_create_req>'
					. '<subject>'. $this->_get_subject($order_info) .'</subject>'
					. '<out_trade_no>'. $order_info['order_sn'] .'</out_trade_no>'
					. '<total_fee>'. $order_info['price'] .'</total_fee>'
					. '<seller_account_name>'. $this->_config['alipay_account'] .'</seller_account_name>'
					. '<notify_url>'. $notify_url .'</notify_url>'
					. '<call_back_url>'. $return_url .'</call_back_url>'
					//. '<out_user>'. $order_info['consignee'] .'</out_user>'
					. '<merchant_url>'. $merchant_url .'</merchant_url>'
					. '<pay_expire>3600</pay_expire>'
					. '</direct_trade_create_req>';

		//构造要请求的参数数组
		$params = array(
			'service'           => 'alipay.wap.trade.create.direct', //接口名称
			'partner'           => $this->_config['alipay_partner'], //合作者身份ID
			'req_id'            => date('Ymdhis').rand(1000,9999), //请求号，唯一
			'sec_id'            => $sign_type, //签名方式
			'req_data'          => $req_data, //请求业务数据
			'format'            => $format, //请求参数格式
			'v'                 => $v, //接口版本号
			"_input_charset"	=> CHARSET
		);
		$params['sign'] = $this->_get_sign($params);	//生成支付签名字符串

		//请求授权接口
		$curl = new Curl ();
		$result = $curl->post($this->_gateway, $params);
		$result = urldecode($result); //URL转码
		//var_dump($result);exit;
		$result_array = explode('&', $result); //根据 & 符号拆分
		//重构数组
		$new_result_array = $temp_item = array();
		if(is_array($result_array)){
			foreach ($result_array as $vo){
				$temp_item = explode('=', $vo, 2); //根据 & 符号拆分
				$new_result_array[$temp_item[0]] = $temp_item[1];
			}
		}

		/**************************根据授权码token调用交易接口alipay.wap.auth.authAndExecute**************************/
		$xml = simplexml_load_string($new_result_array['res_data']);
		$request_token = (array)$xml->request_token;
		$req_data = '<auth_and_execute_req><request_token>'. $request_token[0] .'</request_token></auth_and_execute_req>';
		//请求交易接口参数
		$params = array(
			'service'           => 'alipay.wap.auth.authAndExecute', //接口名称
			'partner'           => $this->_config['alipay_partner'], //合作者身份ID
			'sec_id'            => $sign_type,//签名方式
			'req_data'          => $req_data,//请求数据
			'request_token'     => $request_token[0],
			'format'            => $format, //请求参数格式
			'v'                 => $v, //接口版本号
			'_input_charset'    => CHARSET
		);
		$params['sign'] = $this->_get_sign($params);	//生成支付签名字符串

		
//var_dump($params);exit;
        return $this->_create_payform('GET', $params);
	}

	/**
     *    返回通知结果
     *
     *    
     *    @param     array $order_info
     *    @param     bool  $strict
	 *	  @param     string  $action		add 2014/4/15
     *    @return    array
     */
    function verify_notify($order_info=[], $strict = false,$action=''){
        if (empty($order_info)){
            $this->_error('order_info_empty');
            return false;
        }
        /* 初始化所需数据 (获取支付宝通知返回来的数据)*/
        $notify =   $this->_get_notify();
		if(!empty($notify['notify_data'])){
			$alipay_wap_xml = simplexml_load_string($notify['notify_data']);
			$notify_data = json_decode(json_encode($alipay_wap_xml),TRUE);	//将xml转换为数组
		}//2015/1/21
		//var_export($notify_data);exit;
		$responseTxt = 'false';
		$isSignStr = 'false';
		$error_msg = '';
		$terminal = 'wap';

        /** 
		 * 验证来路是否可信 
		 * 获取远程服务器ATN结果,验证返回URL
		 * @param $notify_id 通知校验ID
		 * @return 服务器ATN结果
		 * 验证结果集：
		 * invalid命令参数不对 出现这个错误，请检测返回处理中partner和key是否为空 
		 * true 返回正确信息
		 * false 请检查防火墙或者是服务器阻止端口问题以及验证时间是否超过一分钟
		 */
        if ($strict){
            /* 严格验证 */
            $verify_result = $this->_query_notify($notify_data['notify_id']);	//2015/1/22
			if($verify_result){//true |false
				$responseTxt = 'true';
			}else{
				$responseTxt = 'false';
			}
            //验证时间超过一分钟失效过滤
            if(!$verify_result && ($order_info['status'] == ORDER_PENDING || $order_info['status'] == ORDER_FAILED)){
				/* 来路不可信 */
				$this->_notifylog($notify,$responseTxt,$isSignStr,$action,$error_msg,$terminal);
				$this->_error('notify_unauthentic');
				return false;
            }
        }

        /* 验证通知是否可信 */
		$notify_type = false;
		if($_GET["sign"]) $notify_type = true;	//get请求
        $sign_result = $this->_verify_sign($notify,$notify_type);
		if(!empty($notify['notify_data'])){
			$notify = $notify_data;	//xml的数组数据重新初始”notify“变量
		}//2015/1/21
		$notify['company_id'] = $order_info['company_id'];
		if($sign_result){
			$isSignStr = 'true';
		}else{
			$isSignStr = 'false';
		}
        if (!$sign_result){
			$error_msg = lang('sign_inconsistent');
            /* 若本地签名与网关签名不一致，说明签名不可信 */
			$this->_notifylog($notify,$responseTxt,$isSignStr,$action,$error_msg,$terminal);
            $this->_error('sign_inconsistent');
            return false;
        }
		//wap 请求的话就直接不查询订单内容了，直接返回
		if(empty($notify['notify_data']) && $notify['result']=='success'){
			unset($notify['company_id']);	//
			$this->_notifylog($notify,$responseTxt,$isSignStr,$action,$error_msg,$terminal);
			return array(
				'status'    =>  ORDER_ACCEPTED,	//已付款
				'pay_notify'=>  $notify,
			);
		}//2015/1/21

        /*----------通知验证结束----------*/

        /*----------本地验证开始----------*/
        /* 验证与本地信息是否匹配 */
        /* 这里不只是付款通知，有可能是发货通知，确认收货通知 */
		//$order_info['out_trade_sn']

        if ($order_info['order_sn'] != $notify['out_trade_no']){ 
			$error_msg = lang('order_inconsistent');
            /* 通知中的订单与欲改变的订单不一致 */
			$this->_notifylog($notify,$responseTxt,$isSignStr,$action,$error_msg,$terminal);
            $this->_error('order_inconsistent');
            return false;
        }
		
        if ($order_info['price'] != $notify['total_fee']){
            /* 支付的金额与实际金额不一致 */
			$error_msg = lang('price_inconsistent');
			$this->_notifylog($notify,$responseTxt,$isSignStr,$action,$error_msg,$terminal);
            $this->_error('price_inconsistent');
            return false;
        }
        //至此，说明通知是可信的，订单也是对应的，可信的

        /* 按支付宝通知结果返回相应的结果 */
        switch ($notify['trade_status']){
		
            case 'WAIT_SELLER_SEND_GOODS':      //买家已付款，等待卖家发货
                $order_status = ORDER_ACCEPTED;
            break;

            case 'WAIT_BUYER_CONFIRM_GOODS':    //卖家已发货，等待买家确认
                $order_status = ORDER_SHIPPED;
            break;
			
			case 'TRADE_FINISHED':
            case 'TRADE_SUCCESS':              //交易结束status
                if ($order_info['status'] == ORDER_PENDING || $order_info['status'] == ORDER_FAILED){
                    /* 如果是等待付款中，则说明是即时到账交易，这时将状态改为已付款 */
                    $order_status = ORDER_ACCEPTED;	//1
                }else{
                    /* 说明是第三方担保交易，交易结束 */
                    $order_status = ORDER_FINISHED;
                }
            break;
            case 'TRADE_CLOSED':                //交易关闭
                $order_status = ORDER_CANCLED;
            break;

            default:
				$error_msg = lang('undefined_status');
                $this->_error('undefined_status');
                return false;
            break;
        }

        switch ($notify['refund_status']){
            case 'REFUND_SUCCESS':              //退款成功，取消订单
                $order_status = ORDER_CANCLED;
            break;
        }
		$this->_notifylog($notify,$responseTxt,$isSignStr,$action,$error_msg,$terminal);

        return array(
            'status'    =>  $order_status,
			'pay_notify'=>  $notify,
        );
    }

    /**
     *    查询通知是否有效
     *	  curl		 形式请求
     *    
     *    @param     string $notify_id
     *    @return    string
     */
    function _query_notify($notify_id){
		$curl = new Curl();
        $query_url = "http://notify.alipay.com/trade/notify_query.do?partner={$this->_config['alipay_partner']}&notify_id={$notify_id}";
		
        return ($curl->get($query_url) === 'true');
    }

    /**
     *    获取签名字符串
     *
     *    @param     array $params
	 *	  @param     bool $is_sort
     *    @return    string
     */
    function _get_sign($params, $is_sort=true){
		if($this->sign_type == 'MD5'){
			$_sign_type =true;
		}
        /* 去除不参与签名的数据*/
        unset($params['sign'],$params['sign_type'], $params['order_id']);
		/* 排序 */
		if($is_sort===true){
			ksort($params);
			reset($params);
		}else{
			$params = $this->sort_notify_para($params);	//异步通知时，对参数做固定排序
		}

        $sign  = '';
        foreach ($params AS $key => $value){
            $sign  .= "{$key}={$value}&";
        }
		//如果存在转义字符，那么去掉转义
		if(get_magic_quotes_gpc()){
			$sign = stripslashes($sign);
		}
		if($_sign_type){//md5方式
			return md5(substr($sign, 0, -1) . $this->_config['alipay_key']);
		}else{
			return substr($sign, 0, -1);
		}
    }

	/**
     * 异步通知时，对参数做固定排序
     * @param $para 排序前的参数组
     * @return 排序后的参数组
     */
	function sort_notify_para($para){
		$para_sort['service'] = $para['service'];
		$para_sort['v'] = $para['v'];
		$para_sort['sec_id'] = $para['sec_id'];
		$para_sort['notify_data'] = $para['notify_data'];

		return $para_sort;
	}

    /**
     *    验证签名是否可信
     *
     *    
     *    @param     array $notify
     *    @return    bool
     */
    function _verify_sign($notify,$is_sort=true){
        $local_sign = $this->_get_sign($notify,$is_sort);	//2014/7/30
		if($this->sign_type=='MD5'){//md5方式
			//echo $local_sign. '==' .$notify['sign'];exit;
			return ($local_sign == $notify['sign']);
		}else{
			//echo $local_sign. '==' .$notify['sign'];exit;
			return $this->rsa_verify($local_sign,$notify['sign']);
		}
    }

	/**RSA签名 
	 * $params待签名数据 
	 * 签名用商户私钥，必须是没有经过pkcs8转换的私钥 
	 * 最后的签名，需要用base64编码 
	 * return Sign签名 
	 */

	function rsa_sign($params) {    
		if(is_array($params)){
			$params = $this->_get_sign($params);
		}

		//读取私钥文件    
		$priKey = file_get_contents(EXTEND_PATH.'payments/wap/alipay/payment_key/rsa_private_key.pem');   
		//转换为openssl密钥，必须是没有经过pkcs8转换的私钥    
		$res = openssl_get_privatekey($priKey);    
		//调用openssl内置签名方法，生成签名$sign    
		openssl_sign($params, $sign, $res);    
		//释放资源    
		openssl_free_key($res);        
		//base64编码    
		$sign = base64_encode($sign);    
		return $sign;
	}

	/**RSA验签 
	 * $params待签名数据 
	 * $sign需要验签的签名 
	 * 验签用支付宝公钥 
	 * return 验签是否通过 bool值 
	 */

	function rsa_verify($params, $sign)  {
		if(is_array($params)){
			$params = $this->_get_sign($params);
		}
		//读取支付宝公钥文件    
		$pubKey = file_get_contents(EXTEND_PATH.'payments/wap/alipay/payment_key/alipay_public_key.pem');    
		//转换为openssl格式密钥    
		$res = openssl_get_publickey($pubKey);    
		//调用openssl内置方法验签，返回bool值    
		//vdump(base64_decode($sign));
		$result = (bool)openssl_verify($params, base64_decode($sign), $res);    
		//释放资源    
		openssl_free_key($res);    
		//返回资源是否成功    
		return $result;
	}





	
}
?>